# This file is part of Email-Reminder.
#
# Email-Reminder is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Email-Reminder is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Email-Reminder; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.

package EmailReminder::YearlyEvent;

use strict;
use warnings;

use Date::Manip;
use POSIX;

use EmailReminder::Event;
use EmailReminder::Utils;

require Exporter;
our @ISA    = ("EmailReminder::Event", "Exporter");
our @EXPORT = qw(get_th get_name get_original_date);

# XML tags, attributes and values
my $DAY_TAG   = 'day';
my $MONTH_TAG = 'month';
my $YEAR_TAG  = 'year';
my $NAME_TAG  = 'name';

# Global date variables
my $current_time = ParseDate("now");
my $current_date = ParseDate(UnixDate($current_time, "\%x"));
my $current_year = UnixDate($current_time, "\%Y");
my $leap_year    = "1980";

# Hard-coded value for this event's type (class method)
sub get_type
{
    return 'yearly';
}

# Number of fields this event adds to its parent (class method)
sub get_nb_fields
{
    return EmailReminder::Event->get_nb_fields() + 2;
}

sub get_name
{
    my ($this) = @_;
    my $name  = EmailReminder::Utils::get_node_value($this->{XML_NODE}, $NAME_TAG);
    return $name;
}

sub set_name
{
    my ($this, $new_value) = @_;
    return EmailReminder::Utils::set_node_value($this->{XML_NODE}, $NAME_TAG, $new_value);
}

sub get_date
{
    my ($this) = @_;

    my $day   = EmailReminder::Utils::get_node_value($this->{XML_NODE}, $DAY_TAG);
    my $month = EmailReminder::Utils::get_node_value($this->{XML_NODE}, $MONTH_TAG);
    my $year  = EmailReminder::Utils::get_node_value($this->{XML_NODE}, $YEAR_TAG);

    # Hack to support events that don't have a starting year
    my $actual_date;
    if (defined($year)) {
        $this->{"MISSING_YEAR"} = 0;
        $actual_date = UnixDate(ParseDate($year ."-".$month."-".$day),
                                "\%Y-\%m-\%d");
    }
    elsif (defined($month) && defined($day)) {
        $this->{"MISSING_YEAR"} = 1;
        my $full_date = UnixDate(ParseDate($leap_year."-".$month."-".$day), 
                                 "\%Y-\%m-\%d");
        $actual_date = UnixDate(ParseDate($full_date), "\%m-\%d");
    }

    return $actual_date;
}

sub set_date
{
    my ($this, $new_value) = @_;
    my $date;

    # Normalize the date entered by the user
    my @slash_parts = split(/\//, $new_value);
    my @dash_parts = split(/-/, $new_value);
    if (@slash_parts == 2) {
        $date = $new_value;
        if ($slash_parts[0] <= 12) {
            # Looks like a MM/DD date, don't parse further
            $date =~ s/(.*)\/(.*)/$1-$2/g;
        } else {
            # Looks like a DD/MM date, don't parse further
            $date =~ s/(.*)\/(.*)/$2-$1/g;
        }
    }
    elsif (@dash_parts == 2) {
        $date = $new_value;
        if ($dash_parts[0] > 12) {
            # Looks like a DD-MM date, don't parse further
            $date =~ s/(.*)-(.*)/$2-$1/g;
        }
    }
    else {
        # Try to parse the date in whatever form the user typed it
        $date = UnixDate(ParseDate($new_value), "\%Y-\%m-\%d");
    }    
    
    if (defined($date)) {
        my @parts = split /-/, $date;
        my $day = pop(@parts);
        my $month = pop(@parts);
        my $year = pop(@parts) if @parts;

        return (EmailReminder::Utils::set_node_value($this->{XML_NODE}, 
                                                     $DAY_TAG, $day) and 
                EmailReminder::Utils::set_node_value($this->{XML_NODE}, 
                                                     $MONTH_TAG, $month) and
                EmailReminder::Utils::set_node_value($this->{XML_NODE}, 
                                                     $YEAR_TAG, $year));
    } else {
        return 0;
    }
}

sub get_original_date
{
    my ($this) = @_;
    my $date_string = $this->get_date();
    return undef unless $date_string;

    $date_string = $leap_year."-".$date_string if $this->{"MISSING_YEAR"};
    return ParseDate($date_string);
}

sub get_subject
{
    my $this = shift;
    return $this->get_name();
}

sub get_message_body
{
    my $this = shift;

    # destination user
    my $first_name = shift;

    # event details
    my $when      = $this->{"WHEN"};
    my $name      = $this->get_name();
    my $occurence = $this->get_occurence();
    my $th        = get_th($occurence);
    my $event     = defined($occurence) ? "${occurence}$th $name" : $name;
    
    my $message = <<MESSAGEEND;
Hi $first_name,

I just want to remind you of the following event $when:

$event
MESSAGEEND

    return $message;
}

# Returns the occurence number of this event (starts at 1)
sub get_occurence
{
    my $this = shift;

    my $exact_age;
    unless ($this->{"MISSING_YEAR"}) {
        my $original_date = $this->get_original_date();
        return undef unless $original_date;

        my $delta = DateCalc($original_date, $current_date, 1);
        $exact_age = Delta_Format($delta, 5, '%yd') + 1;
    }

    return defined($exact_age) ? ceil($exact_age) : undef;
}

# Returns the proper English qualifier for this number
sub get_th
{
    my $number = shift;
    return undef unless defined($number);

    if ($number >= 11 && $number <= 13) {
        return "th";
    } elsif ($number % 10 == 1) {
        return "st";
    } elsif ($number % 10 == 2) {
        return "nd";
    } elsif ($number % 10 == 3) {
        return "rd";
    } else {
        return "th";
    }
}

# Returns 1 if the event will occur in X days (X is a param)
sub will_occur
{
    my $this = shift;
    my $modifier = shift;
    
    # Apply the modifier to the event date
    my $modified_date = $this->get_original_date();
    return 0 unless $modified_date;

    if ($modifier) {
        $modified_date = DateCalc($modified_date, " - $modifier days");
    }
    return 0 unless $modified_date;
    
    my $current_occurence_date = ParseDate(UnixDate($modified_date, "$current_year-\%m-\%d"));
    if (Date_Cmp($current_date, $current_occurence_date) == 0) {
        return 1;
    } else {
        # If an event is scheduled on Feb. 29th, remind on the 28th
        if (UnixDate($modified_date, "\%m-\%d") eq "02-29" and 
            UnixDate($current_date, "\%m-\%d") eq "02-28") {
            return 1;
        } else {
            return 0;
        }
    }
}

1;
